using System;
using System.Windows.Documents;

namespace com.bodurov.SilverlightControls.XmlCodeEditor.Utils
{
    public static class FindUtil
    {
        public static TextPointer GetPointerAtCharacterOffset(TextPointer current, int offsetCharacters)
        {
            var state = current.FoldPointersAfter(
                            new GetPointerAtCharacterOffset_State { Offset = offsetCharacters },
                            (s, tp, r, i, iWithinRun) => s,
                            (s, tp, r, i, iWithinRun) => i >= s.Offset,
                            (s, tp, r, i, iWithinRun) =>
                            {
                                s.Pointer = tp;
                                return s;
                            },
                            (s, tp, r, i, iWithinRun) => s,
                            true
                );
            return state.Pointer;
        }
        internal struct GetPointerAtCharacterOffset_State
        {
            internal int Offset;
            internal TextPointer Pointer;
        }

        public static T FoldPointersAfter<T>(this TextPointer current, T state,
            Func<T, TextPointer, Run, int, int, T> eachInsertionPointer,
            Func<T, TextPointer, Run, int, int, bool> exit,
            Func<T, TextPointer, Run, int, int, T> prepareStateOnExit,
            Func<T, TextPointer, Run, int, int, T> newLine,
            bool skipRunContentStart)
        {
            var newLineLength = Environment.NewLine.Length;
            var indexWithinRun = -1;
            var lastRun = current.Parent as Run;
            if (lastRun != null)
            {
                var start = lastRun.ContentStart;
                while (start != null && start.CompareTo(current) < 0)
                {
                    indexWithinRun++;
                    start = start.GetPositionAtOffset(1, LogicalDirection.Forward);
                }
            }
            if (!current.IsAtInsertionPosition) current = current.GetNextInsertionPosition(LogicalDirection.Forward);
            var count = 0;
            TextPointer temp = current;
            Block lastBlock = FindPreviousBlock(current);
            while (true)
            {
                temp = temp.GetNextInsertionPosition(LogicalDirection.Forward);
                if (temp == null) break;
                var run = temp.Parent as Run;
                if (run == null) continue;
                if (lastRun != null && run != lastRun)
                {
                    indexWithinRun = -1;
                    var block = run.ElementStart.Parent as Block;
                    if (block != null && block != lastBlock)
                    {
                        count += newLineLength;
                        state = newLine(state, temp, run, count, indexWithinRun);
                        if (exit(state, temp, lastRun, count, indexWithinRun))
                        {
                            break;
                        }
                        lastBlock = block;
                    }
                }
                lastRun = run;
                if (run.ContentStart.CompareTo(temp) == 0)
                {
                    continue;
                }
                count++;
                indexWithinRun++;
                if (exit(state, temp, lastRun, count, indexWithinRun))
                {
                    break;
                }
                state = eachInsertionPointer(state, temp, run, count, indexWithinRun);
            }
            return prepareStateOnExit(state, temp, lastRun, count, indexWithinRun);

        }
        public static Block FindPreviousBlock(TextPointer current)
        {
            var temp = current;
            do
            {
                var block = temp.Parent as Block;
                if (block != null) return block;
            }
            while ((temp = temp.GetPositionAtOffset(-1, LogicalDirection.Backward)) != null);
            return null;
        }
    }
}